﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections;
using System.Collections.Generic;

using LeanEngine.Entity;
using LeanEngine.Data.Linq.Extension;

namespace LeanEngine.Data.Linq
{
    /// <summary>
    /// Represents the table from the database to be searched.
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public sealed class QueryableEntity<TEntity> : IQueryable<TEntity>, IQueryProvider, IEnumerable<TEntity>, IEnumerable, IQueryableTable 
        where TEntity : PlainEntity
    {
        #region Declaration Members

        readonly DatabaseContext _dataContext;
        readonly MetaTable _metaTable;

        #endregion

        #region Constructor Members

        internal QueryableEntity(DatabaseContext dataContext, MetaTable metaTable)
        {
            _dataContext = dataContext;
            _metaTable = metaTable;
        }

        #endregion

        #region PropertyInfo Members

        /// <summary>
        /// Gets the data provider which translates the expressions linq queries in T-SQL.
        /// </summary>
        public DatabaseContext DataContext
        {
            get { return _dataContext; }
        }

        #endregion

        #region Overrided Method Members

        /// <summary>
        /// Returns a System.String that represents the current System.Object.
        /// </summary>
        public override string ToString()
        {
            return ("TableToken(" + typeof(TEntity).Name + ")");
        }

        #endregion

        #region IEnumerable<TEntity> Members

        /// <summary>
        /// Gets the result of the T-SQL query which supports a simple iteration over a collection.
        /// </summary>
        public IEnumerator<TEntity> GetEnumerator()
        {
            return ((IEnumerable<TEntity>)_dataContext.Provider.Execute(Expression.Constant(this))).GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Gets the result of the T-SQL query which supports a simple iteration over a collection.
        /// </summary>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        #region IQueryable Members

        /// <summary>
        /// Gets the type of the element(s) that are returned when the expression tree
        /// associated with this instance of System.Linq.IQueryable is executed.
        /// </summary>
        public Type ElementType
        {
            get { return typeof(TEntity); }
        }

        /// <summary>
        /// Gets the expression tree that is associated with this instance.
        /// </summary>
        public Expression Expression
        {
            get { return Expression.Constant(this); }
        }

        /// <summary>
        /// Gets the query provider that is associated with this data source.
        /// </summary>
        public IQueryProvider Provider
        {
            get { return this; }
        }

        #endregion

        #region IQueryProvider Members

        /// <summary>
        /// Constructs an System.Linq.IQueryable object that can evaluate the query represented
        /// by a specified expression tree.
        /// </summary>
        /// <typeparam name="TElement"></typeparam>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            if (!typeof(IQueryable<TElement>).IsAssignableFrom(expression.Type))
                throw new ArgumentException("ExpectedQueryableArgument");

            return new DatabaseQuery<TElement>(_dataContext, expression);
        }

        /// <summary>
        /// Constructs an System.Linq.IQueryable object that can evaluate the query
        /// represented by a specified expression tree.
        /// </summary>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        public IQueryable CreateQuery(Expression expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            var elementType = expression.Type.GetElementType();
            var type2 = typeof(IQueryable<>).MakeGenericType(new[] { elementType });

            if (!type2.IsAssignableFrom(expression.Type)) throw new ArgumentException("ExpectedQueryableArgument");

            return (IQueryable)Activator.CreateInstance(
              typeof(DatabaseQuery<>).MakeGenericType(new[] { elementType }),
              new object[] { _dataContext, expression });
        }

        /// <summary>
        /// Executes the strongly-typed query represented by a specified expression tree.
        /// </summary>
        /// <typeparam name="TResult">The type of the value that results from executing the query.</typeparam>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        public TResult Execute<TResult>(Expression expression)
        {
            return (TResult)_dataContext.Provider.Execute(expression);
        }

        /// <summary>
        /// Executes the query represented by a specified expression tree.
        /// </summary>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        public object Execute(Expression expression)
        {
            return _dataContext.Provider.Execute(expression);
        }

        #endregion

        #region IDatabaseTable Members

        MetaTable IQueryableTable.MetaTable
        {
            get { return _metaTable; }
        }

        #endregion
    }
}
